﻿#include "study05.h"
#include "return_multi_type.h"
#include "ImitationAutoPointer.hpp"
//#include <stddef.h>
#include <stdlib.h>
// C/C++ 学习网站：https://legacy.cplusplus.com 注意旧版本（搜索）和新版本

/*
智能指针：
	智能指针本质上是对普通指针的封装，它可以自动管理堆内存的分配和释放，避免了内存泄漏和资源泄露的问题。
	1.unique_ptr：独占指针，只能有一个指向对象，一旦被释放，就不能再被使用；也叫作用域指针。
		多个指针变量指向一个对象时，销毁一次，其他变量指向内存无效；
	2.shared_ptr：共享指针，可以有多个指向对象，引用计数记录指向对象的数量，
		当一个指向对象被释放时，引用计数减一，引用计数为零时，自动释放对象。
	3.weak_ptr: 弱引用指针，指向 shared_ptr 管理的对象，但不影响引用计数。

构造函数的拷贝：
	C++ 默认提供的拷贝构造函数是拷贝构造函数的签名（指针或引用），也可以理解为浅拷贝。
	我们可以重载拷贝构造函数，实现深拷贝，即拷贝堆内存中的对象到新申请的内存中。

在 C++ 的类中，重载 -> 运算符，可以实现类似于指针的行为，访问堆内存中的对象；
	简单来说，就是实现访问该类的指针成员指向内存中对象的成员的行为（省去 . 运算符）。

C++ 标准库中，有个 vector 类，可以用来管理堆内存，是动态数组的一种封装，可以自动扩容。
	vector 类中，元素的类型可以是任意的，并不是只限于类对象；只是更多用于结构或类对象。
	vector 类中，元素的存储方式是动态分配的，并不一定是连续存储的；特别是存储指针的情况。
	vector 类中，提供了一些方法，可以访问元素，包括 [] 运算符、front()、back()、data() 等。
	vector 类中，提供了一些方法，可以扩容，包括 reserve()、push_back()、emplace_back() 等。
		reserve() 方法可以预先分配足够的内存，push_back() emplace_back() 会在尾部添加元素；
		push_back() 会复制对象，emplace_back() 不会复制对象，直接在堆上创建对象。
	vector 类中，提供了一些方法，可以删除元素，包括 pop_back()、erase() 等。
	vector 类中，提供了一些方法，可以清空元素，包括 clear() 等。

可以使用元组（tuple）来管理堆内存，返回多个类型的值；元组中的元素可以是任意类型，并不一定是类对象。
	元组中的元素可以是指针，也可以是值类型。
	元组中的元素可以是动态分配的，也可以是静态分配的。
	与此类似的 std::pair 类，可以用来管理两个值，也可以用来管理指针和值。

模版类：
	模版类可以定义一个通用接口，可以根据不同类型的数据，生成不同的实现。
	模版类可以定义一个通用算法，可以应用到不同类型的数据上。
	模版类可以定义一个通用容器，可以存储不同类型的数据。
	模版类可以定义一个通用迭代器，可以遍历不同类型的数据。
	模版类可以定义一个通用函数，可以应用到不同类型的数据上。
	C++ 标准库中，有一个 template 类，可以用来替换容器中的元素类型，可以实现泛型编程。
	声明语法：
		template<typename T, typename U...> class MyClass {};
			// 定义模版类，T 为类型参数（可以随意命名）typename 也可以换为 class
			// 当 typename 换为 类型关键字时，T 为类型常量参数
		template<typename T, typename U...> void myFunc(T t, U u) {} // 定义模版函数
	使用语法举例：
		MyClass<int> myIntObj; // 实例化模版类
		myFunc<int, double>(1, 2.0); // 调用模版函数

在内存中，堆区内存大小可以随着程序运行而动态变化，但栈区内存大小是固定的，一般为 2MB 左右。栈区分配内存比堆区快
	预先分配好的堆区内存，在使用时和栈内存一样，拥有高效率。

decltype 关键字：
	decltype 关键字可以用来获取表达式的类型，并不实际分配内存。
	decltype(expression) 表达式的类型，可以用来声明函数模板参数的类型。
	decltype(auto) 关键字，可以自动推导表达式的类型，并不实际分配内存。
	decltype(auto) 关键字只能用于函数模板参数，不能用于函数返回值。

*/

struct FloatValues {
	float x, z, y;
};

struct IntPoint {
	int x, y, z;
	IntPoint() : x(0), y(0), z(0) {} // 默认构造函数
	IntPoint(int dx, int dy, int dz) : x(dx), y(dy), z(dz) {} // 默认构造函数
	IntPoint(const IntPoint& ip)
		: x(0), y(0), z(0) { // 拷贝构造函数
		cout << "copied IntPoint" << endl;
	}
};

//#define offsetof(s, m) ((size_t) &((s*)0)->m) // 标准库的 offsetof 宏，在头文件 <stddef.h> 中
#define my_offset(type, member) ((size_t) &((type*)0)->member)//抄袭，计算结构成员偏移量
//size_t malloc_sum = 0;
//size_t free_sum = 0;
//
//void* operator new(size_t size) {
//	malloc_sum += size;
//	return malloc(size); // 返回地址
//}
//void operator delete(void* ptr, size_t size) {
//	free(ptr); // 调用 free 释放内存
//	free_sum += size;
//}

int main()
{
	// 模拟实现标准库中的 unique_ptr 类，实现堆内存分配和自动释放
	print_start("模拟实现标准库中的 unique_ptr 类，实现堆内存分配和自动释放");
	{
		ScopedPtr ptr1 = new Coordinate(); // new 关键字返回的对象是一个指针
		// 堆内存分配，可写为: ScopedPtr* ptr1(new Coordinate());
	} // 超出 ptr1 作用域，堆内存会释放
	print_end("模拟实现标准库中的 unique_ptr 类，实现堆内存分配和自动释放");

	// 智能指针类的使用测试：
	{
		print_start("智能指针类的使用测试");
		using namespace my_auto_ptr; // 使用自己的命名空间，自己的智能指针类
		// unique_ptr 类的使用:
		{
			std::unique_ptr<Coordinate> ptr2(new Coordinate());// ptr2 指针在堆内存
			// 这里不能写为 ptr2 = new Coordinate()，
			// 因为 unique_ptr 类的构造函数是声明了显示调用的，相当于使用了 explicit 关键字
		//std::unique_ptr<Coordinate> p_copy = ptr2;
		// 不能拷贝 unique_ptr 对象，原理是 unique_ptr 类重载了拷贝构造函数和赋值运算符无效化
			ptr2->setX(1); // 像普通指针一样调用指针指向对象的成员函数
			ptr2->setY(1);
			ptr2->print();
			std::unique_ptr<Coordinate> ptr3 = std::make_unique<Coordinate>(); // 同上 ptr2
			ptr3->setX(2);
			ptr3->setY(2);
			ptr3->print();
			UniquePtr<Coordinate> mp4(new Coordinate()); // 自定义的智能指针，可以管理任意类型的对象
			mp4->setX(3);
			mp4->setY(3);
			//mp4->print();
			UniquePtr<Coordinate> mp5(std::move(mp4)); // 移动构造函数
			//mp5->print(); // 移动后，mp4 指向 nullptr
			UniquePtr<Coordinate> mp6 = std::move(mp5); // 移动构造函数
			mp6->print(); // 移动后，mp5 指向 nullptr
			UniquePtr<Coordinate> mp7 = MakeUnique<Coordinate>(); // 释放 mp6 指向的对象，并返回一个新的 unique_ptr
			mp7->setX(4);
			mp7->setY(4);
			mp7->print(); // 调用智能指针的成员函数
		} // ptr2 和 ptr3 超出作用域，堆内存会自动释放

		// shared_ptr 类的使用:
		{
			std::weak_ptr<Coordinate> weak_ptr; // 声明弱引用指针
			{
				//std::shared_ptr<Coordinate> ptr4(new Coordinate()); // 效率较低
				std::shared_ptr<Coordinate> ptr5 = std::make_shared<Coordinate>();//效率较高
				ptr5->setX(4);
				ptr5->setY(5);
				weak_ptr = ptr5; // 绑定 ptr5 到 weak_ptr，引用计数不会增加
				weak_ptr.lock()->print(); // 解锁 weak_ptr 并调用对象的方法
			} /*调试可以发现，退出这个作用域，ptr5 销毁，但 weak_ptr 仍然绑定着 ptr5
				但此时的 Coordinate 对象已经被销毁了 */
				//weak_ptr.lock()->print(); // 无法访问内存
			weak_ptr.reset(); // 解除绑定

			SharedPtr<Coordinate> ptr6 = MakeShared<Coordinate>(); // 自己的，同上 ptr5
			ptr6->setX(5);
			ptr6->setY(6);
			//ptr6->print();
			{
				SharedPtr<Coordinate> ptr7 = ptr6; // 复制 ptr6 到 ptr7，引用计数增加
				{
					SharedPtr<Coordinate> ptr8 = ptr6; // 复制 ptr6 到 ptr7，引用计数增加
				}
			}
			SharedPtr<Coordinate> ptr9(ptr6);
			std::cout << "ptr9 引用计数：" << ptr9.use_count() << std::endl; // 2
			ptr9->print();
			WeakPtr<Coordinate> my_weak_ptr2 = ptr9; // 绑定 ptr9 到 weak_ptr2，引用计数不会增加
			{
				WeakPtr<Coordinate> my_weak_ptr4 = my_weak_ptr2;
				std::cout << "ptr9 引用计数：" << ptr9.use_count() << std::endl; // 2
				std::cout << "my_weak_ptr2 是否过期：" << my_weak_ptr2.expired() << std::endl; // false 0
				my_weak_ptr4.lock()->print(); // 解锁 weak_ptr2 并调用对象的方法
				my_weak_ptr4.reset(); // 解除绑定
			}
			//for (int i = 0; i < 100000000; i++) // 测试内存泄漏、通过！
			//	WeakPtr<Coordinate> m = my_weak_ptr2;
			//std::cin.get();
			//WeakPtr<Coordinate> my_weak_ptr4(my_weak_ptr2);

		} // 超出作用域，智能指针会自动释放对象堆内存

		print_end("智能指针类的使用测试");
	}

	// 复制构造函数测试：
	test_copy_fuc();

	// 运算符：-> 重载与访问偏移量测试：
	{
		print_start("运算符：-> 重载与访问偏移量测试");
		{
			ScopedPtr ptr6 = new Coordinate();
			ptr6->setX(3);
			ptr6->setY(4);
			ptr6->print();
		}

		FloatValues fv1 = { 1.2f, 2.5f, 3.8f };
		cout << "my_offset(FloatValues, z) = " << my_offset(FloatValues, z) << endl;
		//cout << "my_offset(Coordinate, z) = " << my_offset(Coordinate, y) << endl; // err
			// 使用 offsetof 宏计算偏移量，不能计算带有函数的结构成员的偏移量
		cout << "(size_t)&((FloatValues*)nullptr)->y = "
			<< (size_t) & ((FloatValues*)nullptr)->y << endl; // nullptr 值为 0
		//cout << "(size_t)&((Coordinate*)nullptr)->y = "
		//	<< (size_t)&((Coordinate*)nullptr)->print() << endl; // err

		print_end("运算符：-> 重载与访问偏移量测试");
	}

	// 标准库中的 vector 类测试：
	{
		print_start("标准库中的 vector 类测试");
		std::vector<int> v1; // 不给定元素个数，默认为空
		int i;
		cout << "v1.size() = " << v1.size() << endl; // 0
		for (i = 0; i < 5; ++i) {
			v1.push_back(i + 1);
			cout << "v1[" << i << "] = " << v1[i] << "  ";
		}
		cout << "v1.size() = " << v1.size() << endl;
		cout << endl;

		std::vector<Coordinate> v2(5); // 最好是用对象本身而不是指针，因为指针可能导致内存碎片化
		cout << "v2.size() = " << v2.size() << endl; // 5 个坐标对象（默认构造）已经放入动态数组
		for (i = 0; i < v2.size(); i++) {
			//v2.push_back(Coordinate()); // 会造成死循环
			v2[i].setX(i + 1);
			v2[i].setY(i + 2);
			v2[i].print();
			//if (i >= 10) break;
		}
		// 删除第二个元素，删除后后面的元素前移，并减少元素个数
		v2.erase(v2.begin() + 1); // 注意这里是指针，不是下标
		cout << "v2.size() = " << v2.size() << endl; // 4 个坐标对象
		// range 的写法：
		for (const Coordinate& c : v2) // 引用类型，不写则会发生复制，可以写为 auto 自动推断类型
			c.print(); // 相当于遍历 v2 中的每个坐标对象到 c，然后调用 print()
		v2.clear(); // 清空所有元素

		print_end("标准库中的 vector 类测试");
	}

	// vector 类使用的一些优化：
	{
		print_start("vector 类使用的一些优化");
		std::vector<IntPoint> fv1;
		fv1.reserve(10); // 预分配 10 个 FloatValues 结构体，只改变容量，相当于 malloc
		fv1.push_back(IntPoint(1, 2, 3)); // main 函数内先创建 IntPoint 对象，再 push_back 复制
		fv1.push_back(IntPoint(11, 12, 13));
		fv1.push_back(IntPoint(21, 22, 23));
		fv1.push_back(IntPoint(31, 32, 33)); // 每次进行 push_back 都会进行一次内存分配和复制（复制原来已有的对象数据）
		fv1.emplace_back(41, 42, 43); // 直接在堆上创建对象，不用复制，效率更高
		fv1.emplace_back(51, 52, 53);
		fv1.emplace_back(61, 62, 63);
		fv1.emplace_back(71, 72, 73);
		//int* p = (int*)malloc(sizeof(int) * 10); // 手动分配内存，不用 push_back
		//p = (int*)realloc(p, sizeof(int) * 20); // 手动扩容，不用 push_back
		print_end("vector 类使用的一些优化");
	}

	// 返回多类型值与元组（tuple）测试：
	print_start("返回多类型值与元组（tuple）测试");
	test_return();
	print_end("返回多类型值与元组（tuple）测试");

	// 模版 template 类测试：
	test_template();

	system("pause");
	return 0;
}
